<?php namespace sacvoyage\http;


require_once 'sacvoyage/http/util.php';
use \sacvoyage\http\parse_path;
use \sacvoyage\http\ltrim_path;

use \ArrayIterator, \sacvoyage\Object;

class Collection {

	public function __construct(\sacvoyage\domain\Collection $collection) {
		$this->_collection=$collection;
	}

	public function serve($path) {
		if(!headers_sent()) {
			header('Content-type: application/json', true);
		}
		$path=ltrim_path($path);
		
		if(strtoupper($_SERVER['REQUEST_METHOD']) == 'GET') {
			$this->_get($path);
		} elseif(strtoupper($_SERVER['REQUEST_METHOD']) == 'PUT'
		      || strtoupper($_SERVER['REQUEST_METHOD']) == 'POST') {
			$this->_post($path);
		} elseif(strtoupper($_SERVER['REQUEST_METHOD']) == 'DELETE') {
			$this->_delete($path);
		} else {
			// TODO: set http-request code
			echo '{"status": "error", '.
			     '"message": "'.
				 $_SERVER['REQUEST_METHOD'].' not supported"}';
		}		
	}
	
	private function _parseUrl($path) {
		if(isset($_REQUEST['_query'])) {
			$query=load_json($_REQUEST['_query']);
		} else {
			$query=new Object();
		}
		
		foreach(array('status', 'filter', 'order', 'select', 'reducer') as $param) {
			$new=load_json($_REQUEST['_'.$param]);
			if(isset($_REQUEST['_'.$param]) && !isset($query->$param)) {
				if(isset($query[$param])) {
					$query[$param]=array_merge_recursive_simple($query[$param], $new);
				} else {
					$query[$param]=$new;
				}
			}
		}
		
		$query->singleDocumentExpected=false;
		if($path) {
			if($path=='*') { // all actual versions
				// select all (empty query)
			} elseif(in_array($path, array('~', '~*', '~*', '*~*'))) { // all versions
				$query->status="all";
			} else {
				if(!isset($query->filter)) {
					$query->filter=new Object();
				}
				$parts=explode('~', $path, 2);
				if(count($parts)==1) { // there is no "~"-character in path
					$query->filter->_id=$parts[0];
				} else {
					if(!$parts[1] || $parts[1]=='*') { // .../id~ or .../id~*
						$query->status="all";
					} else {
						$query->filter->_id=$parts[0];
						$query->filter->_rev=$parts[1];
						$query->singleDocumentExpected=true;
					}
				}
			}
		}

		return $query;
	}
	
	
	private function _get($path) {
		$path=urldecode($path);
		if($path) {
			if($path=='*') { // all actual versions
				$iterator=$this->_collection->find();
			} elseif(in_array($path, array('~', '~*', '~*', '*~*'))) { // all versions
				$query=new Object(array('status'=>'all'));
				$iterator=$this->_collection->find($query);
			} else {
				$parts=explode('~', $path, 2);
				if(count($parts)==1) {
					// read returns null if not found
					$document=$this->_collection->read($parts[0]);
					if(!$document) {
						header($_SERVER["SERVER_PROTOCOL"]." 404 Not Found", true);
					}
					echo save_json($document);
					return;
				} else {
					if(!$parts[1] || $parts[1]=='*') { // .../id~ or .../id~*
						$query=new Object(array('status'=>'all'));
						$iterator=$this->_collection->find($query);
					} else {
						$document=$this->_collection->read($parts[0], $parts[1]);
						if(!$document) {
							header($_SERVER["SERVER_PROTOCOL"]." 404 Not Found", true);
						}
						echo save_json($document);
						return;
					}
					
				}
				
			}
		} else { // empty path or some get parameters sent
			$query=new Object();
			if(isset($_REQUEST['_query'])) {
				$query=load_json($_REQUEST['_query']);
			}
			foreach(array('status', 'filter', 'order', 'select', 'reduce') as $param) {
				if(isset($_REQUEST['_'.$param]) && !isset($query->$param)) {
					$query->$param=load_json($_REQUEST['_'.$param]);
				}
			}

			$iterator=$this->_collection->find($query);
		}
		
		// $iterator contains all found documents, now print 'em all
		$once=false;
		echo '[';
		try {
			foreach($iterator as $document) {
				if($once) { 
					echo ',';
				} else {
					$once=true;
				}
				// this is relative exception-safe line,  
				// so the document is either written whole or not
				echo save_json($document);
			}
		} catch (\Exception $e) {
			
			// we cannot notify client about error, do our best
			if(headers_sent()) {
				echo ']';
				echo $e->message();
			} 
			// headers not sent, we can change them and send error-message
			else {
				ob_clean();
				throw $e;
			}
		}
	
		echo ']';
	}
		
	private function _post($path) {
		$path=urldecode($path);
		if(isset($_POST['_data'])) {
			$documents=load_json(urldecode($_POST['_data']));
		} elseif(isset($_FILES) && isset($_FILES['_data'])) {
			$documents=load_json(file_get_contents(
				$_FILES['_data']['tmp_name']
			)); // TODO: check if file too large
		} elseif(isset($_SERVER['Content-Type']) 
		&& starts_with($_SERVER['Content-Type'], 'application/x-www-form-urlencoded')) {
			$document=new Object();
			foreach($_POST as $param=>$value) {
				$document->$param=$value;
			}
			$documents=array($document);
		} else {
			$documents=load_json(file_get_contents('php://input'));
		}
		
		if(!isset($documents)) {
			header($_SERVER["SERVER_PROTOCOL"]." 400 Bad Request", true);
			echo '{"status":"error","message":"document body must be specified"}';
			exit;
		}
		
		// 
		$parts=explode('~', $path, 2);
		if(count($parts)==1) {
			$id=$parts[0];
			$rev=null;
		} else {
			list($id, $rev)=$parts;
		}
		
		if(!is_enumerative_array($documents) && !$documents instanceof \Iterator) {
			$documents=array($documents);
		}

		$result=new Object(array('status'=>'ok', 'written'=>array(), 'errors'=>array(), 'counter'=>0, 'debug'=>array()));
		$result->debug=$documents;
		foreach($documents as $document) {
			
			if(!is_object($document)) {
				$result->status='various';
				$result->errors[]='document must be an object, '.gettype($document).' given';
				continue;
			}
			
			// id's and rev's in document have higher priority
			// than those in url
			if($id && !isset($document->_id)) {
				$document->_id=$id;
			}
			if($rev && !isset($document->_rev)) {
				$document->_rev=$rev;
			}
	
			$result->written=array_merge($result->written, $this->_collection->write($document));
			$result->counter=$result->counter+1;
		}
		
		echo save_json($result);
	}
	
	private function _delete($path) {
		$query=new Object();
		if($path) {
			$path=urldecode($path);
			$parts=explode('~', $path);
			if(count($parts)==1) {
				$query=new Object(array(
					'filter'=>new Object(array(
						'_id'=>$parts[0]
					))
				));
			} else {
				$query=new Object(array(
					'filter'=>new Object(array(
						'_id'=>$parts[0],
						'_rev'=>$parts[1]
					)),
					'status'=>"all"
				));
			}
		} elseif(isset($_REQUEST['_query'])) { //  .../ or .../*\
			$query=load_json($_REQUEST['_query']);
		} elseif(isset($_REQUEST['_filter'])) {
			$query=new Object();
			$query->filter=load_json($_REQUEST['_filter']);
		} elseif($_REQUEST) {
			$query=new Object();
			foreach($_REQUEST as $attr=>$value) {
				$query->$attr=$value;
			}
		}
		$count=$this->_collection->delete($query);
		echo save_json(new Object(array(
			'status'=>'ok', 
			'deleted'=>$count
		)));
	}
	

}


?>